import { encryptPassword, getPasswordSecret } from '../../util/securityUtil'
import log from '../../config/logger'
import { User } from '../../model'
import { BaseService, Context } from '../Service'
import { ServiceNo } from '../ServiceNo'
import * as jwt from 'jsonwebtoken'

class AuthService extends BaseService {
  private readonly excludeInterfaceList: Set<RegExp> = new Set()

  init () {
    let methodNo = 100
    this.addServiceInterface({
      method: this.generateUserToken,
      methodNo: `${methodNo++}`,
      methodDesc: '用户登录接口'
    })
  }

  async generateUserToken (ctx: Context, reqBody: any) {
    const { username, password, email } = reqBody
    const user: any = await User.findOne({
      $or: [
        { username },
        { email }
      ]
    })

    if (user && encryptPassword(password) === user.password) {
      log.info(`user is: ${user.username}`)
      const accessToken = jwt.sign({
        username: user.username,
        password: user.password
      }, getPasswordSecret(user.password), {
        expiresIn: '30d'
      })
      ctx.body = { accessToken }
    } else {
      ctx.throw('用户不存在或密码错误')
    }
  }

  addExcludeInterface (interfas: RegExp) {
    this.excludeInterfaceList.add(interfas)
  }

  needAuthentication (serviceInterfaceNo: string): boolean {
    let need: boolean = true
    for (const value of this.excludeInterfaceList) {
      if (value.test(serviceInterfaceNo)) {
        need = false
        break
      }
    }
    return need
  }

  async authenticate (ctx: Context, token: string) {
    if (!token) { this.throwInvalidToken(ctx) }

    const dtoken: any = jwt.decode(token)
    if (!dtoken) { this.throwInvalidToken(ctx) }

    const vtoken: any = jwt.verify(token, getPasswordSecret(dtoken.password))
    if (vtoken) {
      ctx.user = await User.findOne({
        username: vtoken.username,
        password: vtoken.password
      })
    } else {
      this.throwInvalidToken(ctx)
    }
  }

  throwInvalidToken (ctx: Context) {
    ctx.throw(403, 'invalid token')
  }
}

const authService = new AuthService(ServiceNo.auth)

export default authService

export async function checkUserToken (ctx: Context,
  serviceInterfaceNo: string = ctx.header.service,
  token: string = ctx.header.authorization
) {
  if (authService.needAuthentication(serviceInterfaceNo)) {
    await authService.authenticate(ctx, token)
  }
}

export function addExcludeInterface (interfas: string | ServiceNo) {
  if (interfas.toString().length === 5) {
    authService.addExcludeInterface(new RegExp(`^${interfas}\\d{3}$`))
  } else if (interfas.toString().length === 8) {
    authService.addExcludeInterface(new RegExp(`^${interfas}$`))
  }
}

addExcludeInterface(ServiceNo.auth)
